﻿using System;
using System.Collections.Generic;

using Common.Logging;
using Common.Caching;
using System.Transactions;
using System.Threading;
using System.Security;

using BancoX.Dominio;

namespace BancoX.Servicios
{
    public interface IServicioBancaPersonal
    {
        Transferencia TransferirFondos(string cuentaOrigen, string cuentaDestino, decimal monto);
        Pago PagarPrestacion(string cuenta, string prestacion, string codigo, decimal monto);
    }

    public class ServicioBancaPersonal: IServicioBancaPersonal
    {
        #region Propiedades
        private IRepositorioOperaciones _repositorioOperaciones;
        public IRepositorioOperaciones RepositorioOperaciones
        {
            set { _repositorioOperaciones = value; }
        }

        private IRepositorioCuentas _repositorioCuentas;
        public IRepositorioCuentas RepositorioCuentas
        {
            set { _repositorioCuentas = value; }
        }

        private IRepositorioPrestaciones _repositorioPrestaciones;
        public IRepositorioPrestaciones RepositorioPrestaciones
        {
            set { _repositorioPrestaciones = value; }
        }

        private IAfip _afip;
        public IAfip Afip
        {
            set { _afip = value; }
        }

        private IBancoCentral _bancoCentral;
        public IBancoCentral BancoCentral
        {
            set { _bancoCentral = value; }
        }

        #endregion Propiedades

        #region IServicioBancaPersonal
        public Transferencia TransferirFondos(string origen, string destino, decimal monto)
        {
            if (Thread.CurrentPrincipal == null)
                throw new SecurityException("Usuario no autenticado");

            Transferencia transferencia = null;


            CuentaBancaria cuentaOrigen = _repositorioCuentas.ObtenerCuenta(origen);
            CuentaBancaria cuentaDestino = _repositorioCuentas.ObtenerCuenta(destino);

            if (cuentaOrigen == null)
                throw new CuentaNoValidaException();

            if (cuentaDestino == null)
                throw new CuentaNoValidaException();

            try
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    transferencia = new Transferencia(cuentaOrigen, cuentaDestino, monto);
                    transferencia.RepositorioOperaciones = _repositorioOperaciones;
                    transferencia.BancoCentral = _bancoCentral;
                    transferencia.Afip = _afip;
                    transferencia.Ejecutar();

                    _repositorioOperaciones.RegistrarOperacion(transferencia);
                    scope.Complete();
                    
                    _logger.Info("Transferencia completada." + transferencia.ToString());
                }
            }
            catch (LimiteDiarioAlcanzadoException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            catch (CuentaNoValidaException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            catch (OperacionNoValidaException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            catch (Exception e)
            {
                _logger.Error("Error al transferir fondos.", e);
                throw new OperacionFallidaException(e);
            }
            return transferencia;
        }

        public Pago PagarPrestacion(string numeroCuenta, string idPrestacion, string codigo, decimal monto)
        {
            if (Thread.CurrentPrincipal == null)
                throw new SecurityException("Usuario no autenticado");

            Pago pago = null;

            CuentaBancaria cuenta = _repositorioCuentas.ObtenerCuenta(numeroCuenta);

            if (cuenta == null)
                throw new CuentaNoValidaException();

            Prestacion prestacion = _repositorioPrestaciones.ObtenerPrestacion(idPrestacion);

            if (prestacion == null)
                throw new OperacionNoValidaException("Prestacion inexistente.");

            try
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    pago = new Pago(cuenta, prestacion, codigo, monto);
                    pago.RepositorioOperaciones = _repositorioOperaciones;
                    pago.BancoCentral = _bancoCentral;
                    pago.Afip = _afip;
                    pago.Ejecutar();

                    _repositorioOperaciones.RegistrarOperacion(pago);
                    scope.Complete();
                    _logger.Info("Pago completado." + pago.ToString());
                }
            }
            catch (LimiteDiarioAlcanzadoException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            catch (CuentaNoValidaException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            catch (OperacionNoValidaException ex)
            {
                _logger.Info("Intento de operacion no valida", ex);
                throw;
            }
            return pago;
        }
        #endregion

        #region Infraestructura
        private ILog _logger = LogManager.GetLogger("BancoX.Servicios");
        private ICache _cache = CacheManager.GetCache("BancoX.Servicios");
        #endregion
    }
}
